package com.jin.webvideo.base.spi;

import com.jin.webvideo.base.SpringBeanGetter;
import com.jin.webvideo.base.spi.anno.AutoRegister;
import com.jin.webvideo.base.spi.anno.Register;
import com.jin.webvideo.base.spi.exception.InterfaceNotFoundException;
import com.jin.webvideo.base.spi.exception.NotInitializedException;
import com.jin.webvideo.base.spi.test.TestInterfaceProvider;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;

public abstract class InterfaceProvider {
    private static final Object lock = new Object();

    // installed providers
    private static volatile List<InterfaceProvider> installedProviders;

    // used to avoid recursive loading of instaled providers
    private static boolean loadingProviders  = false;

    private Map<String, Interface> interfaces = new HashMap<>();
    public InterfaceProvider() {
        initInterface();
    }
    /**
     * 接口类型
     * @return
     */
    public abstract String getGroup();

    public abstract void initInterface();
    /**
     *
     * @param type
     * @return
     */
    public Interface getInterface(String type) throws InterfaceNotFoundException {
        synchronized(this.interfaces) {
            Interface i = interfaces.get(type);
            if (i != null) {
                return i;
            }
            throw new InterfaceNotFoundException(type);
        }
    }

    public Set<String> installedInterfaceName() {
        return interfaces.keySet();
    }

    public static InterfaceProvider getDefault() {
        return TestInterfaceProvider.provider();
    }

    public static List<InterfaceProvider>  installedProviders() {
        if (installedProviders == null) {
            // 确保默认接口 已初始化
            InterfaceProvider defaultProvider = getDefault();
            synchronized (lock) {
                if (installedProviders == null) {
                    if (loadingProviders) {
                        throw new Error("Circular loading of installed providers detected");
                    }
                    loadingProviders = true;

                    List<InterfaceProvider> list = AccessController
                            .doPrivileged(new PrivilegedAction<List<InterfaceProvider>>() {
                                @Override
                                public List<InterfaceProvider> run() {
                                    return loadInstalledProviders();
                                }});

                    // 插入默认的提供者
                    list.add(0, defaultProvider);
                    installedProviders = Collections.unmodifiableList(list);
                }
            }
        }
        return installedProviders;
    }

    protected void register(String type, Interface i) {
        synchronized (this.interfaces){
            this.interfaces.put(type, i);
        }
    }

    private static List<InterfaceProvider> loadInstalledProviders() {
        List<InterfaceProvider> list = new ArrayList<>();
        ServiceLoader<InterfaceProvider> providers = ServiceLoader.load(InterfaceProvider.class, Thread.currentThread().getContextClassLoader());
        for (InterfaceProvider provider : providers) {
             String platform = provider.getGroup();
            init(provider);
            if (!"test".equalsIgnoreCase(platform)) {
                boolean found = false;
                for (InterfaceProvider p: list) {
                    if (p.getGroup().equalsIgnoreCase(platform)) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    list.add(provider);
                }
            }
        }
        return list;
    }

    private static void init(InterfaceProvider provider) {
        // 是AutoInterfaceProvider 或 继承自AutoInterfaceProvider，自动注入
        if (provider.getClass() == AutoInterfaceProvider.class || AutoInterfaceProvider.class.isAssignableFrom(provider.getClass())) {
            AutoInterfaceProvider pro = (AutoInterfaceProvider) provider;
            // 判断是否初始化
            if (!pro.init) {
                try {
                    // 获取注解，无注解不处理
                    AutoRegister autoRegister = provider.getClass().getAnnotation(AutoRegister.class);
                    if (autoRegister == null) {
                        return;
                    }
                    pro.setGroup(autoRegister.platform());
                    // 获取需要register 的类
                    Class[] classes = autoRegister.register();
                    for (Class clazz : classes) {
                        // 无注解抛错
                        Register register = (Register) clazz.getAnnotation(Register.class);
                        if (register == null) {
                            throw new NotInitializedException();
                        }
                        Constructor constructor = clazz.getDeclaredConstructor(InterfaceProvider.class);
                        Interface face = (Interface) constructor.newInstance(provider);
                        autowired(face);
                        provider.register(register.name(), face);
                    }
                    pro.init = true;
                }
                catch (Exception exception) {
                    exception.printStackTrace();
                    throw new NotInitializedException();
                }
            }
        }
    }
    private static void autowired(Object obj) {
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if (fields == null) {
            return;
        }
        for (Field field : fields) {
            Autowired autowired = field.getAnnotation(Autowired.class);
            if (autowired == null) {
                continue;
            }
            field.setAccessible(true);
            try {
                if (autowired.required() ) {
                    // 没有bean 会抛异常
                    Object inject = SpringBeanGetter.getBean(field.getType());
                    field.set(obj, inject);
                    continue;
                }
                try {
                    Object inject = SpringBeanGetter.getBean(field.getType());
                    field.setAccessible(true);
                    field.set(obj, inject);
                }
                catch (NoSuchBeanDefinitionException e) {

                }
            }
            catch (IllegalAccessException e) {
                e.printStackTrace();
                throw new NotInitializedException();
            }
        }
    }
}
